/*
 * @Author: dadadaXU 1413107032@qq.com
 * @Date: 2024-12-28 21:10:55
 * @LastEditors: dadadaXU 1413107032@qq.com
 * @LastEditTime: 2025-01-04 22:34:28
 * @FilePath: /FileBackup/src/fileBackupUnit.cpp
 * @Description: 文件打包相关的基本操作
 */
#include "../include/fileBaseUnit.hpp"

bool FileBaseUnit::ReadBackupHeader(BackupHeader &backup_header)
{
    if (!this->read((char *)(&backup_header), TAR_RECORD_SIZE))
    {
        std::cerr << "Read file header failed! " << this->filePath << std::endl;
        return false;
    }
    /* BackupHeader 是否存在 */
    if (backup_header.filePath[0] == '\0' &&
        backup_header.offset != sizeof(BackupHeader))
        return false;

    return true;
}

void FileBaseUnit::WriteFileHeader(const FileHeader &file_header)
{
    char buffer[TAR_RECORD_SIZE] = {0};
    std::memcpy(buffer, &file_header, TAR_RECORD_SIZE);
    this->write((const char *)buffer, TAR_RECORD_SIZE);

    /* 计算文件头CRC */
    std::memset((buffer + TAR_FILE_PATH), 0, SIZE_CRC_CODE); // 抹去CRC
    this->fileHeader.CRC ^=
        Calc_CRC32((uint8_t *)(buffer), TAR_RECORD_SIZE);
}

bool FileBaseUnit::ReadFileHeader(FileHeader &file_header)
{
    char buffer[TAR_RECORD_SIZE] = {0};
    this->read((char *)&buffer, TAR_RECORD_SIZE);
    std::memcpy(&file_header, buffer, TAR_RECORD_SIZE);

    /* 检查 `filePath`与 `metadata` 是否为空 */
    if (file_header.filePath[0] == '\0' &&
        file_header.metadata.st_size == 0 &&
        file_header.metadata.st_mode == 0)
        return false;

    /* 计算文件头CRC */
    std::memset((buffer + TAR_FILE_PATH), 0, SIZE_CRC_CODE); // 抹去CRC
    this->fileHeader.CRC ^=
        Calc_CRC32((uint8_t *)(buffer), TAR_RECORD_SIZE);

    return true;
}

void FileBaseUnit::WriteDataBlock(FileBaseUnit &source_file)
{
    char buffer[TAR_RECORD_SIZE] = {0};
    size_t file_size = source_file.fileHeader.metadata.st_size;
    uint32_t file_CRC = 0;
    std::streampos file_head_pos = this->tellp() - std::streamoff(TAR_RECORD_SIZE);

    while (file_size >= TAR_RECORD_SIZE && source_file.peek() != EOF)
    {
        source_file.read(buffer, TAR_RECORD_SIZE);
        this->write(buffer, TAR_RECORD_SIZE);
        file_size -= TAR_RECORD_SIZE;
        /* 累加buffer计算CRC */
        file_CRC ^= Calc_CRC32((uint8_t *)(buffer), TAR_RECORD_SIZE);
    }
    if (file_size > 0) // 最后一块数据
    {
        std::memset(buffer, 0, TAR_RECORD_SIZE); // 清空 buffer
        source_file.read(buffer, file_size);
        this->write(buffer, TAR_RECORD_SIZE); // 整块写入
        /* 累加buffer计算CRC */
        file_CRC ^= Calc_CRC32((uint8_t *)(buffer), file_size);
    }

    this->fileHeader.CRC ^= file_CRC;        // 整个打包文件
    this->WriteCRC(file_CRC, file_head_pos); // 每个文件的校验码
}

bool FileBaseUnit::RestoreNormalFileData(FileBaseUnit &file_data)
{
    char buffer[TAR_RECORD_SIZE] = {0};
    size_t file_size = this->fileHeader.metadata.st_size;
    uint32_t block_num = this->fileHeader.blockNum;
    uint32_t file_CRC = 0;

    while (file_size >= TAR_RECORD_SIZE && file_data.peek() != EOF)
    {
        file_data.read(buffer, TAR_RECORD_SIZE);
        this->write(buffer, TAR_RECORD_SIZE);
        file_size -= TAR_RECORD_SIZE;
        block_num--;
        /* 累加buffer计算CRC */
        file_CRC ^= Calc_CRC32((uint8_t *)(buffer), TAR_RECORD_SIZE);
    }
    if (file_size > 0) // 最后一块数据
    {
        file_data.read(buffer, TAR_RECORD_SIZE); // 整块读取
        this->write(buffer, file_size);
        block_num--;
        /* 累加buffer计算CRC */
        file_CRC ^= Calc_CRC32((uint8_t *)(buffer), file_size);
    }

    if (block_num < 0)
    {
        std::cerr << "Restore file data failed! " << this->filePath << std::endl;
        return false;
    }

    if (file_CRC != this->fileHeader.CRC)
    {
        std::cerr << "Restore file data failed! -> " << file_data.filePath << std::endl
                  << "\tOriginal CRC: " << this->fileHeader.CRC << std::endl
                  << "\tRestore CRC:  " << file_CRC << std::endl;
        return false;
    }
    file_data.fileHeader.CRC ^= file_CRC; // 整个文件的校验码

    return true;
}

void FileBaseUnit::WriteSymbolicLink(const std::filesystem::path &file_path)
{
    std::filesystem::path link_target =
        std::filesystem::absolute(
            std::filesystem::read_symlink(file_path))
            .lexically_normal(); // 读取符号链接

    this->write(link_target.c_str(), TAR_RECORD_SIZE);

    // /* 计算链接模块CRC */
    // this->fileHeader.CRC ^=
    //     Calc_CRC32((uint8_t *)(buffer), TAR_RECORD_SIZE);
}

void FileBaseUnit::WriteHardLink(const std::filesystem::path &file_path)
{
    std::filesystem::path link_target =
        std::filesystem::absolute(file_path).lexically_normal();
    this->write(link_target.c_str(), TAR_RECORD_SIZE);
}

std::filesystem::path FileBaseUnit::ReadFileLink()
{
    char buffer[TAR_RECORD_SIZE] = {0};
    this->read(buffer, TAR_RECORD_SIZE);

    // /* 计算链接模块CRC */
    // this->fileHeader.CRC ^=
    //     Calc_CRC32((uint8_t *)(buffer), TAR_RECORD_SIZE);

    return std::filesystem::path(buffer);
}

void FileBaseUnit::RestoreMetadata()
{
    int result = 0;
    result = ::chmod(this->fileHeader.filePath,
                     this->fileHeader.metadata.st_mode);
    if (result == -1)
    {
        std::cerr << "chmod failed: " << this->fileHeader.filePath << std::endl;
    }
    result = 0;

    result = ::lchown(this->fileHeader.filePath,
                      this->fileHeader.metadata.st_uid,
                      this->fileHeader.metadata.st_gid);
    if (result == -1)
    {
        std::cerr << "lchown failed: " << this->fileHeader.filePath << std::endl;
    }
    result = 0;

    timespec original_time[2] = {
        this->fileHeader.metadata.st_atim,
        this->fileHeader.metadata.st_mtim};
    result = ::utimensat(AT_FDCWD, this->fileHeader.filePath, original_time, AT_SYMLINK_NOFOLLOW);
    if (result == -1)
    {
        std::cerr << "chmod failed: " << this->fileHeader.filePath << std::endl;
    }
}